using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using System.Media;
using GameLibrary;
 
namespace ArkanoidGame
{
   #region Enums
   public enum GameState
   {
       Ready,
       Playing,
       LevelComplete,
 
       GameOverFailed,
       GameOverSuccess
   }
   #endregion

   public class ArkanoidGame: GamePanel
   {
       #region Private Members
       private Ball m_ball = null;
       private Paddle m_paddle = null;
       private List<Brick> m_bricks = new List<Brick>();
       private BricksHUD m_hud = null;
       private List<Bonus> m_bonuses = new List<Bonus>();
       private GameState m_state = GameState.Ready;
       private MouseState m_oldMS;
       private MouseState m_currentMS;
       #endregion
 
       #region Public Properties
       public GameState State
       {
           get { return m_state; }
           set { m_state = value; }
       }
 
       public Ball Ball
       {
           get { return m_ball; }
           set { m_ball = value; }
       }
 
       public List<Brick> Bricks
       {
           get { return m_bricks; }
       }
 
       public Paddle Paddle
       {
           get { return m_paddle; }
           set { m_paddle = value; }
       }
 
       public BricksHUD HUD
       {
           get { return m_hud; }
       }

       public List<Bonus> Bonuses 
       {
           get { return m_bonuses; }
       }

 
       public int ConsecutiveHits { get; set; }
       #endregion

 
       #region Constructors
       public ArkanoidGame(int initialWidth, int initialHeight)
           : base(initialWidth, initialHeight)
       {
           InitializeGame();
       }
       #endregion
 
       #region Private Methods
       private void InitializeGame()
       {
           this.ConsecutiveHits = 0;
 
           if (m_hud == null)
           {
               m_hud = new BricksHUD(
                   this,
                   new PointF(0f, this.InitialGameSize.Height - (float)BricksSettings.HUD_HEIGHT),
                   new SizeF(this.InitialGameSize.Width - 1, (float)BricksSettings.HUD_HEIGHT - 1));
           }
 
           this.HUD.Lives = BricksSettings.LIVES_DEFAULT;
           this.HUD.Level = 1;
           this.HUD.ScoreAccumulator = 0;
           this.HUD.Score = 0;
           this.HUD.ScoreMultiplier = 1;
 
           m_paddle = new Paddle(
               this,
               new PointF(this.InitialGameSize.Width / 2f - (float)BricksSettings.PADDLE_WIDTH / 2f, this.HUD.Rectangle.Top - BricksSettings.PADDLE_DISTANCE_FROM_HUD - BricksSettings.PADDLE_HEIGHT),
               new SizeF(BricksSettings.PADDLE_WIDTH, BricksSettings.PADDLE_HEIGHT)
           );
 
           m_ball = new Ball(
               this,
               new PointF(m_paddle.Rectangle.Left + m_paddle.Rectangle.Width / 2f - (float)BricksSettings.BALL_WIDTH / 2f, m_paddle.Rectangle.Top - (float)BricksSettings.BALL_HEIGHT - BricksSettings.BRICK_PADDING_Y),
               new SizeF(BricksSettings.BALL_WIDTH, BricksSettings.BALL_HEIGHT)
           );

           LoadLevel();
       }
 
       private bool LoadLevel()
       {
           bool levelLoaded = true;
           m_bricks.Clear();

           int levelNumber = this.HUD.Level - 1;
           string exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
           string levelsPath = Path.Combine(exePath, "Levels");

           DirectoryInfo levelsDir = new DirectoryInfo(levelsPath);
           FileInfo[] levelsFiles = levelsDir.GetFiles();
           int levelsQuantity = levelsFiles.Length;

           if (levelNumber >= levelsQuantity) 
           { 
               return levelLoaded = false; 
           }

           List<string> rowsList = new List<string>();

           using (StreamReader reader = new StreamReader(levelsFiles[levelNumber].FullName))
           {
                   do
                   {
                       rowsList.Add(reader.ReadLine());
                   }
                   while (reader.Peek() != -1);
           }

           string[] level = rowsList.ToArray();

           float startY = 50f;
           float startX = this.InitialGameSize.Width / 2f - (float)(level[0].Length * (BricksSettings.BRICK_WIDTH + BricksSettings.BRICK_PADDING_X)) / 2f;

           for (int i = 0; i < level.Length; i++)
           {
               for (int j = 0; j < level[i].Length; j++)
               {
                   if (level[i][j] == '1')
                   {
                       m_bricks.Add(
                                     new Brick(
                                         this,
                                         new PointF(startX + (float)(j * (BricksSettings.BRICK_WIDTH + BricksSettings.BRICK_PADDING_X)), startY + (float)(i * (BricksSettings.BRICK_HEIGHT + BricksSettings.BRICK_PADDING_Y))),
                                         new SizeF((float)BricksSettings.BRICK_WIDTH, (float)BricksSettings.BRICK_HEIGHT)
                                     )
                                 ); 
                   }
               }
           }
 
           return levelLoaded;
       }
       #endregion
 
       #region GamePanel Implementation
       protected override void OnUpdate(TimeSpan elapsedTime)
       {
           m_currentMS = this.Mouse.GetState();
 
           switch (m_state)
           {
               case GameState.Ready:
                   m_paddle.Update(elapsedTime);
                   m_ball.Update(elapsedTime);
                   m_bonuses.Clear();
 
                   if (m_oldMS.IsButtonDown(MouseButtons.Left) && !m_currentMS.IsButtonDown(MouseButtons.Left))
                   {
                       m_ball.Velocity = new PointF(m_ball.CalculateNewHorizontalVelocity(m_ball.Position), BricksSettings.BALL_INITIAL_VELOCITY_Y);
                       this.State = GameState.Playing;
                   }
 
                   break;
 
               case GameState.Playing:
                   m_ball.Update(elapsedTime);
                   m_paddle.Update(elapsedTime);

                   foreach (Bonus bonus in m_bonuses.ToList())
                   {
                           bonus.Update(elapsedTime);
                   } 

                   foreach (Brick brick in m_bricks)
                   { 
                       brick.Update(elapsedTime); 
                   }
 
                   break;
 
               case GameState.GameOverSuccess:
               case GameState.GameOverFailed:
                   if (this.HUD.FloatingTextItems.Count == 0 && m_oldMS.IsButtonDown(MouseButtons.Left) && !m_currentMS.IsButtonDown(MouseButtons.Left))
                   {
                       InitializeGame();
                       this.State = GameState.Ready;
                   }
                   break;
 
               case GameState.LevelComplete:
                   if (this.HUD.FloatingTextItems.Count == 0)
                   {
                       this.HUD.Level++;
                       if (!LoadLevel())
                       {
                           m_hud.UpdateHighScores();
                           string text = "You win!";
                           Font font = new Font(this.Font.FontFamily, 20, FontStyle.Bold);
                           SizeF textSize = this.CreateGraphics().MeasureString(text, font);
 
                           this.HUD.FloatingTextItems.Add(
                               new FloatText(
                                   this,
                                   text,
                                   font,
                                   BricksSettings.OUTLINE_COLOR,
                                   new PointF(this.InitialGameSize.Width / 2f - textSize.Width / 2f, this.InitialGameSize.Height / 2f - textSize.Height / 2f),
                                   new PointF(0f, -15f),
                                   new TimeSpan(0, 0, 3)
                               )
                           );
                           this.State = GameState.GameOverSuccess;
                           using (var soundPlayer = new SoundPlayer(Properties.Resources.victory3))
                           {
                               soundPlayer.Play();
                           }
                           HUD.UpToDate = false;
                       }
                       else
                       {
                           this.State = GameState.Ready;
                       }
                   }
                   break;
           }
 
           m_hud.Update(elapsedTime);
           m_oldMS = m_currentMS;
       }
 
       protected override void OnDraw(TimeSpan elapsedTime, Graphics g)
       {
           // Draw background
           g.FillRectangle(Brushes.Black, this.ClientRectangle);
 
           
           m_ball.Draw(elapsedTime, g);
           m_paddle.Draw(elapsedTime, g);

           
           foreach (Bonus bonus in m_bonuses)
           {
                bonus.Draw(elapsedTime, g);
           }
           

           foreach (Brick brick in m_bricks)
           {
                brick.Draw(elapsedTime, g);
           }
                   
           m_hud.Draw(elapsedTime, g);
 
       }
       #endregion
   }
}